# -*- coding: utf-8 -*-
# ㊎ Author : XMZ
# ㊍ Date:  : 2024/01/10 13:37
# ㊌ File   : script-framework->logger.py
# ㊋ IDE    : PyCharm
# ㊏ REMARKS:
import os
import sys
import logging
import traceback
import logging.handlers
from datetime import datetime
from functools import lru_cache
from pathlib import Path
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler

sys.path.append(str(Path(__file__).absolute().parent.parent.parent))
from core import settings
from publics.pushNotification import pushMessage

"""
SingletonLogConfig.setup(__file__)
"""


class SingletonLogConfig:
    _instance = None
    _original_logger = logging.getLogger()

    @classmethod
    def setup_global_exception_handler(cls):
        """
        设置全局异常处理器，以便捕获所有未处理的异常。
        """

        def handle_exception(exc_type, exc_value, exc_traceback):
            # 当异常为 SystemExit 时不处理
            if issubclass(exc_type, (SystemExit, KeyboardInterrupt)):
                sys.__excepthook__(exc_type, exc_value, exc_traceback)
                return
            # 获取格式化的异常信息
            exc_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
            # 使用自定义logger记录异常信息
            cls._instance.logger.error("未捕获异常: %s", exc_info)

        sys.excepthook = handle_exception

    @classmethod
    @lru_cache(maxsize=1)
    def setup(cls, filename):
        if cls._instance is None:
            cls._instance = cls(filename)

        # 设置全局异常处理器
        cls.setup_global_exception_handler()

        # 设置全局日志记录器为已配置的实例
        logging.root = cls._instance.logger

        # 获取自定义 logger
        logger = cls._instance.logger

        # 控制台日志处理器，输出所有级别的日志
        if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
            console_handler = logging.StreamHandler()
            console_handler.setLevel(logging.DEBUG)
            formatter = CustomFormatter()
            console_handler.setFormatter(formatter)
            logging.root.addHandler(console_handler)

        # 配置日志文件路径
        if not os.path.exists(settings.LOG_PATH): os.mkdir(settings.LOG_PATH)
        log_file_name = f"{str(os.path.basename(filename))[:-3]}.log"
        log_path = os.path.join(settings.LOG_PATH, log_file_name)
        # 配置日志文件设置
        if not any(isinstance(h, RotatingFileHandler) for h in logger.handlers):
            file_handler = RotatingFileHandler(
                log_path, maxBytes=2 * 1024 * 1024, backupCount=3, encoding='utf-8'
            )

            file_handler.setLevel(logging.WARNING)
            formatter = CustomFormatter()
            file_handler.setFormatter(formatter)
            logging.root.addHandler(file_handler)

        # 添加钉钉错误处理器
        if not any(isinstance(h, DingTalkErrorHandler) for h in logger.handlers):
            error_handler = DingTalkErrorHandler()
            error_handler.setLevel(logging.ERROR)
            logging.root.addHandler(error_handler)

    def __init__(self, filename):
        logger_name = f'{os.path.splitext(os.path.basename(filename))[0]}'
        self.logger = logging.getLogger(logger_name)
        self.logger.setLevel(logging.DEBUG)


class CustomFormatter(logging.Formatter):
    def format(self, record):
        levelname = f'{record.levelname: <8}'  # 使用制表符填充以保持列对齐
        module_line = f'{os.path.basename(record.pathname)}[line:{record.lineno}]'
        message = record.getMessage()
        asctime = self.formatTime(record)
        return f"{levelname} {asctime} {module_line}: {message}"


class DingTalkErrorHandler(logging.Handler):
    def __init__(self):
        super().__init__(logging.ERROR)
        # 核心优化：设置 CustomFormatter
        self.setFormatter(CustomFormatter())

    def emit(self, record):
        # self.format(record) 现在会使用上面设置的 CustomFormatter
        message = self.format(record)

        # 优化：对于异常日志，我们应该发送完整的堆栈信息，如果 record 中包含 exc_info
        if record.exc_info:
            # 格式化完整的异常堆栈信息
            formatted_exc = self.formatter.formatException(record.exc_info)
            # 将格式化的日志消息和堆栈信息拼接起来
            full_message = f"{message}\n{formatted_exc}"
        else:
            full_message = message

        # 只有在非调试模式且不是 KeyboardInterrupt 时才发送通知
        if settings.debug == "True":
            return

        # 使用 full_message 而不是原始的 message
        if "KeyboardInterrupt" not in full_message:
            # 假设 push_notif 能够处理较长的消息
            pushMessage("项目骨架", full_message)
